package com.example.demo.service.impl;

import com.example.demo.domain.online.*;
import com.example.demo.entity.Online;
import com.example.demo.exception.NotFoundException;
import com.example.demo.exception.UnknownException;
import com.example.demo.mapper.OnlineMapper;
import com.example.demo.service.OnlineProjectService;
import com.example.demo.service.OnlineService;
import com.example.demo.unitls.RedisKeyGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 网签配置服务层实现
 *
 * @author 叮当猫的百宝箱
 * @since 1.0
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Lazy))
public class OnlineServiceImpl implements OnlineService {

    private final OnlineMapper onlineMapper;

    private final RestTemplate restTemplate;

    private final RedisTemplate<String, Serializable> redisTemplate;

    private final OnlineProjectService onlineProjectService;

    private static String token;

    /**
     * 添加新的网签配置
     *
     * @param online 待添加的网签配置信息
     */
    @Override
    public void createOnline(Online online) {
        LocalDateTime now = LocalDateTime.now();
        online.setCreateTime(now);
        online.setUpdateTime(now);
        onlineMapper.insert(online);
    }

    /**
     * 根据网签配置ID删除一个网签配置
     *
     * @param id 网签配置ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOnlineById(Long id) {

        // TODO 如果要删除网签配置可能要检查是否有其他业务用到了这个网签配置;
        //  如果正在使用的网签配置不应该被直接删除

        onlineMapper.deleteById(id);
        onlineProjectService.deleteOnlineProjectByOnlineId(id);
    }

    /**
     * 获取全部网签配置列表
     *
     * @return 全部网签配置列表
     */
    @Override
    public List<Online> getAllOnline() {
        return onlineMapper.findAll();
    }

    /**
     * 根据网签配置ID获取网签配置信息
     *
     * @param id 网签配置ID
     * @return 网签配置信息
     */
    @Override
    public Online getOnlineById(Long id) {
        String cacheKey = RedisKeyGenerator.generateOnlineKey(id);
        Online online = (Online) redisTemplate.opsForValue().get(cacheKey);

        if (online == null) {
            online = onlineMapper.findById(id);
            if (online != null) {
                redisTemplate.opsForValue().set(cacheKey, online);
            }
        }

        return online;
    }

    /**
     * 根据网签配置ID更新网签配置信息
     *
     * @param id     网签配置ID
     * @param online 等待更新网签配置信息
     */
    @Override
    public void updateOnlineById(Long id, Online online) {
        // 要先确认要更新的网签配置得存在
        Online existed = getOnlineById(id);
        if (existed == null) {
            throw new NotFoundException("未知的网签配置!");
        }

        Online updateOnline = new Online();
        BeanUtils.copyProperties(online, updateOnline);

        // 这里清空掉创建时间并且重制更新时间为当前时间
        updateOnline.setCreateTime(null);
        updateOnline.setUpdateTime(LocalDateTime.now());
        onlineMapper.updateSelectiveById(id, updateOnline);
    }

    /**
     * 根据网签配置ID获取第三方房token信息
     *
     * @param id 网签配置ID
     * @return 网签平台token信息
     */
    @Override
    public String getOnlineTokenById(Long id) {
        Online online = getOnlineById(id);
        return getOnlineTokenById(online);
    }

    /**
     * 根据网签配置获取第三方房token信息
     *
     * @param online 网签配置信息
     * @return 网签平台token信息
     */
    @Override
    public String getOnlineTokenById(Online online) {
        String api = online.getUrl().concat("/oauth/v2/token");

        // 创建一个 MultiValueMap 对象，用于构建表单参数
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();

        requestBody.add("client_id", online.getClientId());
        requestBody.add("client_secret", online.getClientSecret());
        requestBody.add("grant_type", "password");
        requestBody.add("scope", "read");
        requestBody.add("username", online.getUsername());
        requestBody.add("password", online.getPassword());

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<OnlineResponseResult<TokenData>> responseEntity = restTemplate.exchange(
                api,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<OnlineResponseResult<TokenData>>() {
                }
        );

        OnlineResponseResult<TokenData> response = responseEntity.getBody();

        if (response == null || !response.getCode().equals(0)) {
            throw new UnknownException("网签授权失败");
        }

        TokenData data = response.getData();

        // 将token设置到静态变量中
        setToken(data.getAccessToken());
        return token;
    }

    /**
     * 请求网签中心获取网签合同编码
     *
     * @param id                网签配置ID
     * @param projectId         网签项目ID
     * @param projectName       网签中心项目名称
     * @param contractAttribute 合同属性: 个人(01),单位(02),过渡期(03)
     * @return 合同编码
     */
    @Override
    public String getOnlineContractNo(Long id, String projectId, String projectName, String contractAttribute) {
        Online online = getOnlineById(id);

        if (token == null) {
            getOnlineTokenById(online);
        }

        String api = online.getUrl().concat("/business/commonhouse/openapi/getContractSeqNo");

        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("access_token", token);
        requestBody.add("companyId", online.getCompanyId());
        requestBody.add("companyName", online.getCompanyName());
        requestBody.add("PID", projectId);
        requestBody.add("projectName", projectName);
        requestBody.add("contractAttribute", contractAttribute);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<OnlineResponseResult<ContractNoData>> responseEntity = restTemplate.exchange(
                api,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<OnlineResponseResult<ContractNoData>>() {
                }
        );

        OnlineResponseResult<ContractNoData> response = responseEntity.getBody();

        if (response == null || !response.getCode().equals(0)) {
            throw new UnknownException("获取网签合同编码失败");
        }

        ContractNoData data = response.getData();
        return data.getContractNo();

    }

    /**
     * 根据身份证号获取网签准入资格证编码
     *
     * @param id     网签配置ID
     * @param cardNo 要查询的身份证号码
     * @return 准入资格证编码列表
     */
    @Override
    public List<String> getOnlineTransactionNoByCardNo(Long id, String cardNo) {
        Online online = getOnlineById(id);
        if (token == null) {
            getOnlineTokenById(online);
        }

        String api = online.getUrl().concat("/business/commonhouse/openapi/getTransactionInfo");

        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("access_token", token);
        requestBody.add("companyId", online.getCompanyId());
        requestBody.add("companyName", online.getCompanyName());
        requestBody.add("cardNo", cardNo);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<OnlineResponseResult<List<TransactionInfoData>>> responseEntity = restTemplate.exchange(
                api,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<OnlineResponseResult<List<TransactionInfoData>>>() {
                }
        );

        OnlineResponseResult<List<TransactionInfoData>> response = responseEntity.getBody();

        if (response == null || !response.getCode().equals(0)) {
            throw new UnknownException("获取网签准入资格证编码失败");
        }

        List<TransactionInfoData> data = response.getData();

        return data.stream().map(TransactionInfoData::getTransactionNo).collect(Collectors.toList());
    }

    /**
     * 网签申请退租
     *
     * @param id          网签配置ID
     * @param projectId   网签项目ID
     * @param projectName 网签项目名称
     * @param houseId     网签房源ID
     * @param contractNo  网签合同编码
     * @param operator    操作人
     */
    @Override
    public void applyTermination(Long id, String projectId, String projectName, String houseId, String contractNo, String operator) {
        log.error("即将调用网签中心申请退租接口,项目ID:{},项目名称:{},房源ID:{},合同编码:{},操作人: {}",
                projectId, projectName, houseId, contractNo, operator);

        Online online = getOnlineById(id);
        if (token == null) {
            getOnlineTokenById(online);
        }

        String api = online.getUrl().concat("/business/commonhouse/openapi/returnContrart");

        // 构造请求的参数
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("access_token", token);
        requestBody.add("companyId", online.getCompanyId());
        requestBody.add("companyName", online.getCompanyName());
        requestBody.add("PID", projectId);
        requestBody.add("projectName", projectName);
        requestBody.add("HID", houseId);
        requestBody.add("contractNO", contractNo);
        requestBody.add("operator", operator);

        // 请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<OnlineResponseResult<ReturnContractData>> responseEntity = restTemplate.exchange(
                api,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<OnlineResponseResult<ReturnContractData>>() {
                }
        );
        OnlineResponseResult<ReturnContractData> response = responseEntity.getBody();
        if (response == null || !response.getCode().equals(0)) {
            throw new UnknownException("申请退租失败");
        }
    }

    /**
     * 网签确认退租
     *
     * @param id            网签配置ID
     * @param projectId     网签项目ID
     * @param projectName   网签项目名称
     * @param houseId       网签房源ID
     * @param contractNo    网签合同编码
     * @param operator      操作人
     * @param livingEndTime 实际退租时间
     * @param houseInfo     房屋信息
     */
    @Override
    public void confirmTermination(Long id, String projectId, String projectName, String houseId, String contractNo, String operator, String livingEndTime, HouseInfoData houseInfo) {
        log.error("即将调用网签中心确认退租接口,项目ID:{},项目名称:{},房源ID:{},合同编码:{},操作人: {},房屋信息:{}",
                projectId, projectName, houseId, contractNo, operator, houseInfo);

        Online online = getOnlineById(id);
        if (token == null) {
            getOnlineTokenById(online);
        }

        String api = online.getUrl().concat("/business/commonhouse/openapi/confirmReturnContrart");

        // 这里不能使用 MultiValueMap
        // 原因: MultiValueMap 主要用于处理 HTTP 请求参数、表单数据等，其中一个键可能对应多个值的情况。
        // 使用了 MultiValueMap 转换为 Json 格式数据时会以数组的形式转换，例如:{"companyId": ["015"]}
        // 这回导致请求接口的数据错误。
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("companyId", online.getCompanyId());
        requestBody.put("companyName", online.getCompanyName());
        requestBody.put("PID", projectId);
        requestBody.put("projectName", projectName);
        requestBody.put("HID", houseId);
        requestBody.put("contractNO", contractNo);
        requestBody.put("operator", operator);
        requestBody.put("livingEndTime", livingEndTime);
        requestBody.put("returnhouseInfoBean", houseInfo);

        // 请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 确认退租接口需要将access_token 放在header中
        headers.add("access_token", token);

        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);

        ResponseEntity<OnlineResponseResult<ConfirmReturnContractData>> responseEntity = restTemplate.exchange(
                api,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<OnlineResponseResult<ConfirmReturnContractData>>() {
                }
        );
        OnlineResponseResult<ConfirmReturnContractData> response = responseEntity.getBody();
        if (response == null || !response.getCode().equals(0)) {
            throw new UnknownException("确认申请退租失败");
        }
    }


    /**
     * 网签续租确认
     *
     * @param id          网签配置ID
     * @param projectId   网签项目ID
     * @param projectName 网签项目名称
     * @param houseId     网签房源ID
     * @param contractNo  老合同编码
     * @param operator    操作人
     */
    @Override
    public void confirmResignContract(Long id, String projectId, String projectName, String houseId, String contractNo, String operator) {
        log.error("即将调用网签中心网签续租确认接口,项目ID:{},项目名称:{},房源ID:{},合同编码:{},操作人: {}",
                projectId, projectName, houseId, contractNo, operator);

        Online online = getOnlineById(id);
        if (token == null) {
            getOnlineTokenById(online);
        }

        String api = online.getUrl().concat("/business/commonhouse/openapi/confirmResignContract");
        // 构造请求的参数
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        requestBody.add("access_token", token);
        requestBody.add("companyId", online.getCompanyId());
        requestBody.add("companyName", online.getCompanyName());
        requestBody.add("PID", projectId);
        requestBody.add("projectName", projectName);
        requestBody.add("HID", houseId);
        requestBody.add("contractNO", contractNo);
        requestBody.add("operator", operator);

        // 请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
        ResponseEntity<OnlineResponseResult<ConfirmResignContractData>> responseEntity = restTemplate.exchange(
                api,
                HttpMethod.POST,
                requestEntity,
                new ParameterizedTypeReference<OnlineResponseResult<ConfirmResignContractData>>() {
                }
        );
        OnlineResponseResult<ConfirmResignContractData> response = responseEntity.getBody();
        if (response == null || !response.getCode().equals(0)) {
            throw new UnknownException("确认续签失败");
        }
    }

    /**
     * 设置token
     *
     * @param token 网签中心的accessToken信息
     */
    protected void setToken(String token) {
        OnlineServiceImpl.token = token;
    }


}
